數據驅動是 vue 的核心概念之一,不直接操作 DOM 元素,而是通過修改 data,再把 data 渲染成 DOM。$mount()
就是負責把 data 掛載到 Vue 實例渲染成 DOM。
//vue/src/platforms/web/entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
/*********************************************
把 指定的 DOM 元素 賦值給 el 參數
*********************************************/
el = el && query(el)
const options = this.$options
if (!options.render) {
let template = options.template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
...
}
} else if (template.nodeType) {
template = template.innerHTML
} else {
...
return this
}
} else if (el) {
template = getOuterHTML(el)
}
if (template) {
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile')
}
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile end')
measure(`vue ${this._name} compile`, 'compile', 'compile end')
}
}
}
return mount.call(this, el, hydrating)
}
首先用 mount 變數把原本 Vue 實例上的 $mount 暫存起來,再重新定義 compiler 時的 $mount
若 options 裡有 render 方法, 直接调用 mount.call(this, el, hydrating),即是調用 compiler 時的 $mount
若 options 裡沒有 render 方法:
若有 template,則調用 compileToFunctions
編譯成 render 方法
若沒有 template,則判斷有沒有 el ,有就轉換成 template,再調用 compileToFunctions
編譯成 render 方法
把 render 方法掛載到 options 裡
調用 mount.call(this, el, hydrating),即是調用原本 Vue 實例上的 $mount
//src/platforms/web/runtime/index.js
// public mount method
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
第一個載入點在 src/platforms/web/runtime/index.js
上,
在 Vue 實例上定義 $mount,這裡的 $mount 是公用方法,可以在 runtime only
和 runtime+compiler
時使用
// src/core/instance/lifecycle.js
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
/*********************************************
把 el 參數 賦值給 vm.$el
*********************************************/
vm.$el = el
/*********************************************
若沒有 render 方法,把 createEmptyVNode 作為 render 方法
*********************************************/
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
if (process.env.NODE_ENV !== 'production') {
...
}
callHook(vm, 'beforeMount')
let updateComponent
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
updateComponent = () => {
const name = vm._name
const id = vm._uid
const startTag = `vue-perf-start:${id}`
const endTag = `vue-perf-end:${id}`
mark(startTag)
const vnode = vm._render()
mark(endTag)
measure(`vue ${name} render`, startTag, endTag)
mark(startTag)
vm._update(vnode, hydrating)
mark(endTag)
measure(`vue ${name} patch`, startTag, endTag)
}
} else {
/*********************************************
定義 updateComponent 方法,執行 vm._render 返回 vnode,vnode 當參數傳入 vm._update 中,渲染成真實的 DOM
*********************************************/
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
}
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true )
hydrating = false
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}